﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using JasLib.Extensions;

namespace JasLib.Threading
{
    public abstract class AsyncSynchronizationPrimitive : FinalizedDisposableObject
    {
        protected class OccupantInfo
        {
            public int _iRecursionCount = 0;
        }

        protected readonly DisposePatternMonitorLock _objLock = new DisposePatternMonitorLock();
        private Dictionary<CancellationToken, CancellationTokenRegistration> _cancellationTokensToRegistrations = new Dictionary<CancellationToken, CancellationTokenRegistration>();

        protected AsyncSynchronizationPrimitive(bool bEnableTaskRecursion)
        {
        }

        protected int GetCurrentTaskId()
        {
            int? iCurrentTaskId = Task.CurrentId;
            if (!iCurrentTaskId.HasValue)
                throw new InvalidOperationException("SemaphoreGate is only designed to be called from within a running task.");
            return iCurrentTaskId.Value;
        }

        /// <summary>
        /// When an associated cancellation token gets signalled, the SemaphoreGate disposes.
        /// </summary>
        public void AddCancellationToken(CancellationToken token)
        {
            using (_objLock.Lock())
            {
                EnforceNotDisposed();

                if (_cancellationTokensToRegistrations != null)
                {
                    var registration = token.Register(this.OnTokenCancellationPrivate, token /* reminder: this is a boxing operation */, false);
                    _cancellationTokensToRegistrations.Add(token, registration);
                }
            }
            return;
        }

        protected override void Dispose(bool bFinalizer)
        {
            var stolenCancellationTokensToRegistrations = Interlocked.Exchange(ref _cancellationTokensToRegistrations, null);
            if (stolenCancellationTokensToRegistrations != null)
            {
                stolenCancellationTokensToRegistrations.Values.ForEach(r => r.Dispose());
                stolenCancellationTokensToRegistrations.Clear();
            }

            return;
        }

        private void OnTokenCancellationPrivate(object objToken)
        {
            OnTokenCancellation((CancellationToken)objToken);
            return;
        }

        protected virtual void OnTokenCancellation(CancellationToken token)
        {
        }

        public abstract Task WaitAsync(TimeSpan? waitTime = null);
    }

    public abstract class AsyncQueuedSynchronizationPrimitive : AsyncSynchronizationPrimitive
    {
        public AsyncQueuedSynchronizationPrimitive(bool bEnableTaskRecursion)
            :base(bEnableTaskRecursion)
        {
            return;
        }
    }
}
